package top.jiangqiang.qianyi.common.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.collection.CollUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.server.PathContainer;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 实现此过滤器之后，只需要注入容器即可生效，免去其余配置，并且可以精确控制请求路径
 * includePatterns为空，则拦截所有路径
 * order默认为0，优先级越小越高
 * excludePatterns优先级比includePatterns更高
 */
@Slf4j
public abstract class AbstractPathMatchingFilter extends OncePerRequestFilter {

    protected final PathPatternParser pathPatternParser = new PathPatternParser();
    protected final Set<PathPattern> includePatterns = new HashSet<>();
    protected final Set<PathPattern> excludePatterns = new HashSet<>();

    /**
     * 拦截所有路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter() {
    }

    /**
     * @param includePatterns 拦截指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(String... includePatterns) {
        addIncludePatterns(includePatterns);
    }

    /**
     * @param includePattern  拦截指定路径，使用springboot3.0默认的路径解析规则
     * @param excludePatterns 排除指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(String includePattern, String[] excludePatterns) {
        addIncludePatterns(includePattern);
        addExcludePatterns(excludePatterns);
    }

    /**
     * @param includePattern  拦截指定路径，使用springboot3.0默认的路径解析规则
     * @param excludePatterns 排除指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(String includePattern, List<String> excludePatterns) {
        addIncludePatterns(includePattern);
        addExcludePatterns(excludePatterns);
    }

    /**
     * @param includePatterns 拦截指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(List<String> includePatterns) {
        addIncludePatterns(includePatterns);
    }

    /**
     * @param includePatterns 拦截指定路径，使用springboot3.0默认的路径解析规则
     * @param excludePatterns 排除指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(List<String> includePatterns, String... excludePatterns) {
        addIncludePatterns(includePatterns);
        addExcludePatterns(excludePatterns);
    }

    /**
     * @param includePatterns 拦截指定路径，使用springboot3.0默认的路径解析规则
     * @param excludePatterns 排除指定路径，使用springboot3.0默认的路径解析规则
     */
    public AbstractPathMatchingFilter(List<String> includePatterns, List<String> excludePatterns) {
        addIncludePatterns(includePatterns);
        addExcludePatterns(excludePatterns);
    }

    @Override
    protected final void doFilterInternal(@NotNull HttpServletRequest request,
                                          @NotNull HttpServletResponse response,
                                          @NotNull FilterChain filterChain) throws IOException, ServletException {
        String requestURI = request.getRequestURI();
        String contextPath = request.getContextPath();
        String path = requestURI.startsWith(contextPath)
                ? requestURI.substring(contextPath.length())
                : requestURI;
        PathContainer pathContainer = PathContainer.parsePath(path);

        // 检查排除模式
        if (isMatch(pathContainer, excludePatterns)) {
            filterChain.doFilter(request, response);
            return;
        }
        //包含路径为空，匹配全部
        if (CollUtil.isEmpty(includePatterns) || isMatch(pathContainer, includePatterns)) {
            doEnhancedFilterInternal(request, response, filterChain);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    /**
     * 子类实现的具体过滤逻辑
     */
    protected abstract void doEnhancedFilterInternal(HttpServletRequest request,
                                                     HttpServletResponse response,
                                                     FilterChain filterChain) throws IOException, ServletException;

    private boolean isMatch(PathContainer path, Set<PathPattern> patterns) {
        if (CollUtil.isEmpty(patterns)) {
            return false;
        }
        return patterns.stream().anyMatch(pattern -> pattern.matches(path));
    }

    public void addIncludePatterns(String... includePatterns) {
        this.includePatterns.addAll(Arrays.stream(includePatterns)
                .map(pathPatternParser::parse)
                .toList());
    }

    public void addIncludePatterns(List<String> includePatterns) {
        this.includePatterns.addAll(includePatterns.stream()
                .map(pathPatternParser::parse)
                .toList());
    }

    public void addExcludePatterns(String... excludePatterns) {
        this.excludePatterns.addAll(Arrays.stream(excludePatterns)
                .map(pathPatternParser::parse)
                .toList());
    }

    public void addExcludePatterns(List<String> excludePatterns) {
        this.excludePatterns.addAll(excludePatterns.stream()
                .map(pathPatternParser::parse)
                .toList());
    }
}
